Analyze Data
SQL joins disparate datasets
Queries expose relationship
Adjust columns for table/graph needs
Department of Energy’s EIA: Monthly Energy Review &
NOAA Mauna Loa, Hawaii Observatory - Carbon PPM
SELECT p.date_year, p.date_month, CONCAT(p.date_year, '-', p.date_month, '-01')::date as date_day,
p.average_ppm as "carbon ppm", c.energy_consumed as "btu consumed", e.energy_co2 as "co2 emissions"
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
ORDER BY p.date_year, p.date_month
SELECT p.date_year,
sum(p.average_ppm) as carbon_ppm_total,
avg(p.average_ppm) as carbon_ppm_mean,
sum(c.energy_consumed) as btu_consumed_total,
avg(c.energy_consumed) as btu_consumed_mean,
sum(e.energy_co2) as co2_emissions_total,
avg(e.energy_co2) as co2_emissions_mean
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
GROUP BY p.date_year
ORDER BY p.date_year
kable_styling(kable(tail(agg_df, 10)),
bootstrap_options = c("striped", "hover"))
| |
date_year |
carbon_ppm_total |
carbon_ppm_mean |
btu_consumed_total |
btu_consumed_mean |
co2_emissions_total |
co2_emissions_mean |
| 38 |
2010 |
4678.79 |
389.8992 |
6641.355 |
553.4462 |
5585.741 |
465.4784 |
| 39 |
2011 |
4699.83 |
391.6525 |
6473.666 |
539.4722 |
5446.133 |
453.8444 |
| 40 |
2012 |
4726.24 |
393.8533 |
5684.503 |
473.7086 |
5237.300 |
436.4417 |
| 41 |
2013 |
4758.25 |
396.5208 |
6689.368 |
557.4473 |
5363.018 |
446.9182 |
| 42 |
2014 |
4783.77 |
398.6475 |
7007.139 |
583.9283 |
5411.193 |
450.9327 |
| 43 |
2015 |
4810.01 |
400.8342 |
6465.092 |
538.7577 |
5264.776 |
438.7313 |
| 44 |
2016 |
4850.87 |
404.2392 |
6033.098 |
502.7582 |
5172.402 |
431.0335 |
| 45 |
2017 |
4878.64 |
406.5533 |
6111.580 |
509.2983 |
5130.589 |
427.5491 |
| 46 |
2018 |
4902.26 |
408.5217 |
6896.516 |
574.7097 |
5267.750 |
438.9792 |
| 47 |
2019 |
410.83 |
410.8300 |
1163.200 |
1163.2000 |
493.839 |
493.8390 |
metric_ts <- xts(x=metrics_df[c("carbon ppm", "btu consumed", "co2 emissions")],
order.by=metrics_df$date_day)
plot(metric_ts, main = "Energy and Carbon PPM Metrics",
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1)

Seasonal Decomposition
carbonppm_ts <- ts(metrics_df$`carbon ppm`, start=c(1973, 1), frequency=12)
carbonppm_stl <- stl(carbonppm_ts, s.window="periodic")
plot(carbonppm_stl, col = seaborn_palette[1],
main="Seasonal Decomposition of Global Carbon PPM")

consumed_ts <- ts(metrics_df$`btu consumed`, start=c(1973, 1), frequency=12)
consumed_stl <- stl(consumed_ts, s.window="periodic")
plot(consumed_stl, col = seaborn_palette[2],
main="Seasonal Decomposition of U.S. Energy Consumption")

emissions_ts <- ts(metrics_df$`co2 emissions`, start=c(1973, 1), frequency=12)
emissions_stl <- stl(emissions_ts, s.window="periodic")
plot(emissions_stl, col = seaborn_palette[3],
main = "Seasonal Decomposition of U.S. CO2 Emissions")

sql <- "WITH c1 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Primary Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Primary Energy Consumed\"
FROM consumption
WHERE msn IN ('TXICBUS', 'TXRCBUS', 'TXACBUS', 'TXCCBUS', 'TXEIBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
), c2 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Total Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Total Energy Consumed\"
FROM consumption
WHERE msn IN ('TECCBUS', 'TEACBUS', 'TEICBUS', 'TERCBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
)
SELECT c1.decade, c1.\"Sector\", c1.\"Primary Energy Consumed\", c2.\"Total Energy Consumed\"
FROM c1
LEFT JOIN c2
ON c1.\"Sector\" = c2.\"Sector\" AND c1.decade = c2.decade
ORDER BY c1.decade, c1.\"Sector\"
"
consumed_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(consumed_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Primary Energy Consumed |
Total Energy Consumed |
| 35 |
2000s |
Transportation Sector |
546616.33 |
548112.4 |
| 36 |
2010s |
Commercial Sector |
77171.71 |
326503.7 |
| 37 |
2010s |
Electric Power Sector |
693591.66 |
NA |
| 38 |
2010s |
Industrial Sector |
388661.21 |
570178.3 |
| 39 |
2010s |
Residential Sector |
117157.95 |
378492.5 |
| 40 |
2010s |
Transportation Sector |
490149.36 |
491557.4 |
Consumption and CO2 Emissions
plot_mat <- with(subset(consumed_df, Sector != 'Electric Power Sector'),
tapply(`Total Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Total Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 8E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

plot_mat <- with(consumed_df, tapply(`Primary Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Primary Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 9E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32, 41), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

sql <- "SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(REPLACE(description, 'Total Energy ', ''), ' CO2 Emissions', '') AS \"Sector\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM us_co2_emissions
WHERE msn IN ('TERCEUS', 'TECCEUS', 'TEACEUS', 'TXEIEUS')
GROUP BY date_year, description
ORDER BY date_year, description
"
emissions_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(emissions_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Total CO2 Emissions |
| 183 |
2010s |
Residential Sector |
2034.691 |
| 184 |
2010s |
Transportation Sector |
3830.401 |
| 185 |
2010s |
Commercial Sector |
164.804 |
| 186 |
2010s |
Electric Power Sector |
274.988 |
| 187 |
2010s |
Residential Sector |
223.165 |
| 188 |
2010s |
Transportation Sector |
295.360 |
plot_mat <- with(emissions_df, tapply(`Total CO2 Emissions`, list(decade, `Sector`), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. CO2 Emissions by Sector", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 6E4), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3, 9, 15, 21)+0.5, labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE($1, '%', '') as sector,
REPLACE(
REPLACE(
REPLACE(
REPLACE(description, 'Commercial Sector CO2 Emissions', ''),
'Residential Sector CO2 Emissions', ''
), 'Transportation Sector CO2 Emissions', ''),
' ', '\n') AS \"Type\",
energy_co2
FROM us_co2_emissions
WHERE description LIKE $2)
SELECT decade, sector, \"Type\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM sub
GROUP BY decade, sector, \"Type\"
ORDER BY decade, sector, \"Type\"
"
params <- paste0(c("%Transportation", "%Residential", "%Commercial"), " Sector%")
emissions_type_df <- do.call(rbind, lapply(params, function(p) dbGetQuery(conn, sql, param=list(p, p))))
par(mfrow=c(3,2), mar=c(5, 5, 2, 1), mai = c(0.7, 0.2, 0.7, 0.2))
output <- by(emissions_type_df, emissions_type_df$sector, function(sub) {
plot_mat <- with(sub, tapply(`Total CO2 Emissions`, list(decade, `Type`), sum))
barplot(plot_mat, main=paste("U.S. CO2 Emissions by", sub$sector[[1]]), cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, ceiling(max(plot_mat, na.rm=TRUE)/1E4) * 1E4),
xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3,9,15,21,27,33,39,45,51,57,63)[seq_along(colnames(plot_mat))] + 0.5,
labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)
})

Renewable Energy: Production and Consumption
sql <- "SELECT energy_type,
date,
SUM(production) AS production,
SUM(consumption) AS consumption
FROM us_renewable_energy
GROUP BY energy_type,
date
ORDER BY energy_type,
date
"
renewable_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(renewable_df)),
bootstrap_options = c("striped", "hover"))
| |
energy_type |
date |
production |
consumption |
| 4678 |
Wood Energy |
2019-02-01 |
190.887 |
182.491 |
| 4679 |
Wood Energy |
2019-03-01 |
198.621 |
191.507 |
| 4680 |
Wood Energy |
2019-04-01 |
195.791 |
187.670 |
| 4681 |
Wood Energy |
2019-05-01 |
201.743 |
193.775 |
| 4682 |
Wood Energy |
2019-06-01 |
198.379 |
189.036 |
| 4683 |
Wood Energy |
2019-07-01 |
205.023 |
196.873 |
par(mfrow=c(3,3), mar=c(5, 5, 2, 1))
output <- by(renewable_df, renewable_df$energy_type, function(sub) {
metric_ts <- xts(x=sub[c("production", "consumption")],
order.by=sub$date)
print(plot(metric_ts, main = sub$energy_type[1],
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. Geological Survey - Groundwater Well Depth Data
sql <- "SELECT g.year, g.month,
avg(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY g.year, g.month"
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
month |
mean_value |
| 350 |
2019 |
2 |
63735.69 |
| 351 |
2019 |
3 |
60792.13 |
| 352 |
2019 |
4 |
54698.44 |
| 353 |
2019 |
5 |
53237.05 |
| 354 |
2019 |
6 |
49160.99 |
| 355 |
2019 |
7 |
76941.00 |
boxplot(mean_value ~ year, groundwater_df, col=seaborn_palette[1:10],
main="Groundwater Well Depth Mean Values", cex.main=1.5)

sql <- "WITH sites AS (
SELECT CONCAT(g.year, '-', g.month, '-1')::date AS \"date\",
g.site_name,
AVG(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY CONCAT(g.year, '-', g.month, '-1')::date,
g.site_name
), bottom AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'bottom_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) ASC LIMIT 5
), top AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'top_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) DESC LIMIT 5
)
SELECT sites.\"date\", sites.mean_value, sites.site_name, top.category
FROM sites
INNER JOIN top
ON sites.site_name = top.site_name
UNION
SELECT sites.\"date\", sites.mean_value, sites.site_name, bottom.category
FROM sites
INNER JOIN bottom
ON sites.site_name = bottom.site_name
ORDER BY category, site_name, \"date\""
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(head(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| date |
mean_value |
site_name |
category |
| 1991-06-01 |
31 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-07-01 |
35 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-08-01 |
41 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-09-01 |
42 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-10-01 |
36 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-11-01 |
40 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
groundwater_df$year <- format(groundwater_df$date, "%Y")
par(mfrow=c(5,2), mar=c(5, 5, 2, 1))
output <- by(groundwater_df, groundwater_df$site_name, function(sub) {
metric_ts <- xts(x=sub[c("mean_value")],
order.by=sub$date)
print(plot(metric_ts, main = paste(sub$site_name[1], ':', sub$category[1]),
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. FWS Endangered Species List
sql <- "SELECT CONCAT((f.date_year/10)::int * 10, 's') AS decade,
f.taxonomic_group,
f.current_status,
count(*) AS species_count
FROM fws_species_year f
WHERE f.current_status IN ('Endangered', 'Recovery', 'Resolved Taxon',
'Threatened', 'Extinction')
GROUP BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status
ORDER BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status"
fws_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(fws_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
taxonomic_group |
current_status |
species_count |
| 141 |
2010s |
Mammals |
Resolved Taxon |
1 |
| 142 |
2010s |
Mammals |
Threatened |
11 |
| 143 |
2010s |
Reptiles |
Endangered |
4 |
| 144 |
2010s |
Reptiles |
Threatened |
9 |
| 145 |
2010s |
Snails |
Endangered |
18 |
| 146 |
2010s |
Snails |
Threatened |
1 |
par(mfrow=c(5,3), mar=c(5, 5, 2, 1))
output <- by(fws_df, fws_df$taxonomic_group, function(sub) {
plot_mat <- with(sub, tapply(species_count, list(decade, current_status), sum))
barplot(plot_mat, main=sub$taxonomic_group[[1]], cex.main=1.5,
ylim = c(0, max(plot_mat, na.rm=TRUE)+5),
col=seaborn_palette[seq_along(row.names(plot_mat))], beside=TRUE)
legend("top", row.names(plot_mat), fill=seaborn_palette[seq_along(row.names(plot_mat))],
ncol=length(row.names(plot_mat)))
box()
})

U.S. Department of Agriculture: Agriculture Census
sql <- "SELECT year,
CASE
WHEN domain_category = '' THEN 'TOTAL\nOPERATIONS'
ELSE REPLACE(REPLACE(REPLACE(domain_category, '(', '\n('), 'TO', 'TO\n'), 'OR', 'OR\n')
END AS domain_category,
value
FROM ag_census
WHERE data_item = 'FARM OPERATIONS - NUMBER OF OPERATIONS'"
agcensus_df <- dbGetQuery(conn, sql)
plot_mat <- with(agcensus_df, tapply(value, list(year, domain_category), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Farm Operations", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 2E6+5E5), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS (
SELECT year,
data_item,
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(domain_category, ';', ','),
'(', '\n('),
'TO', 'TO\n'),
' OR', ' OR\n'),
'LESS', 'LESS\n') AS domain_category,
value
FROM ag_census
WHERE data_item LIKE '%COMMODITY TOTALS%'
AND value IS NOT NULL
)
SELECT year,
CASE
WHEN TRIM(domain_category) = ''
THEN CASE
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $'
THEN 'TOTAL\nSALES'
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $ / OPERATION'
THEN 'TOTAL\nSALES\nPER OPERATION'
END
ELSE domain_category
END AS domain_category,
value
FROM sub
"
agcensus_df <- within(dbGetQuery(conn, sql),
domain_category <- factor(domain_category,
levels = c("FARM SALES: \n(LESS\n THAN 2,500 $)", "FARM SALES: \n(2,500 TO\n 4,999 $)",
"FARM SALES: \n(5,000 TO\n 9,999 $)", "FARM SALES: \n(10,000 TO\n 24,999 $)",
"FARM SALES: \n(25,000 TO\n 49,999 $)", "FARM SALES: \n(50,000 TO\n 99,999 $)",
"FARM SALES: \n(100,000 TO\n 499,999 $)", "FARM SALES: \n(500,000 OR\n MORE $)",
"TOTAL\nSALES", "TOTAL\nSALES\nPER OPERATION"))
)
kable_styling(kable(tail(agcensus_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
domain_category |
value |
| 45 |
2017 |
FARM SALES:
(5,000 TO
9,999 $) |
208074 |
| 46 |
2017 |
FARM SALES:
(10,000 TO
24,999 $) |
228218 |
| 47 |
2017 |
FARM SALES:
(25,000 TO
49,999 $) |
144113 |
| 48 |
2017 |
FARM SALES:
(50,000 TO
99,999 $) |
119434 |
| 49 |
2017 |
FARM SALES:
(100,000 TO
499,999 $) |
218771 |
| 50 |
2017 |
FARM SALES:
(500,000 OR
MORE $) |
146568 |
plot_mat <- with(agcensus_df[agcensus_df$domain_category != 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 1E6), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45,51)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

plot_mat <- with(agcensus_df[agcensus_df$domain_category == 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Overall Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 4E11+5E10), beside=TRUE, xaxt="n", yaxt="n", space=2)
axis(side=1, at=c(2.5,5.5,8.5,11.5,14.5), labels=row.names(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
REPLACE(REPLACE(data_item, ' - OPERATIONS WITH AREA HARVESTED', ''), '; GRAIN', '') as crop,
value
FROM ag_census
WHERE data_item IN ('CORN; GRAIN - OPERATIONS WITH AREA HARVESTED',
'WHEAT - OPERATIONS WITH AREA HARVESTED',
'SOYBEANS - OPERATIONS WITH AREA HARVESTED')
ORDER BY year, data_item
"
crops_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(crops_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
crop |
value |
| 10 |
2012 |
CORN |
348530 |
| 11 |
2012 |
SOYBEANS |
302963 |
| 12 |
2012 |
WHEAT |
147632 |
| 13 |
2017 |
CORN |
304801 |
| 14 |
2017 |
SOYBEANS |
303191 |
| 15 |
2017 |
WHEAT |
104792 |
plot_mat <- with(crops_df, tapply(value,
list(year, factor(crop, levels=c("WHEAT", "SOYBEANS", "CORN"))), sum)
)
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Operations by Specific Crops", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 5E5), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
REPLACE(
REPLACE(
REPLACE(data_item, ' - OPERATIONS WITH SALES', ''),
'; INCL CALVES', ''),
'; BROILERS', '') as livestock,
value
FROM ag_census
WHERE data_item IN ('CATTLE; INCL CALVES - OPERATIONS WITH SALES',
'HOGS - OPERATIONS WITH SALES',
'CHICKENS; BROILERS - OPERATIONS WITH SALES')
ORDER BY year, data_item
"
livestock_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(livestock_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
livestock |
value |
| 10 |
2012 |
CATTLE |
740978 |
| 11 |
2012 |
CHICKENS |
32935 |
| 12 |
2012 |
HOGS |
55882 |
| 13 |
2017 |
CATTLE |
711827 |
| 14 |
2017 |
CHICKENS |
32751 |
| 15 |
2017 |
HOGS |
64871 |
plot_mat <- with(livestock_df, tapply(value,
list(year, factor(livestock, levels=c( "CHICKENS", "HOGS", "CATTLE"))), sum)
)
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Operations by Specific Livestock", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 1E6+2E5), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "SELECT year,
CASE
WHEN data_item = 'CROP TOTALS - SALES; MEASURED IN $' THEN 'CROP TOTALS'
WHEN data_item = 'ANIMAL TOTALS; INCL PRODUCTS - SALES; MEASURED IN $' THEN 'ANIMAL TOTALS'
END as category,
value
FROM ag_census
WHERE data_item IN ('CROP TOTALS - SALES; MEASURED IN $',
'ANIMAL TOTALS; INCL PRODUCTS - SALES; MEASURED IN $')
ORDER BY year, data_item
"
agtotal_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(agtotal_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
category |
value |
| 5 |
2007 |
ANIMAL TOTALS |
153562563000 |
| 6 |
2007 |
CROP TOTALS |
143657928000 |
| 7 |
2012 |
ANIMAL TOTALS |
182247407000 |
| 8 |
2012 |
CROP TOTALS |
212397074000 |
| 9 |
2017 |
ANIMAL TOTALS |
194975996000 |
| 10 |
2017 |
CROP TOTALS |
193546699000 |
plot_mat <- with(agtotal_df, tapply(value, list(year, category), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Crop vs Animal Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 2E11+5E10), yaxt="n", beside=TRUE)
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

World Metrics
sql <- "WITH pop AS
(SELECT p.year,
p.population
FROM world_population p
WHERE p.country_name = 'World'
AND p.year BETWEEN 2000 AND 2019
),
land AS
(SELECT a.year,
a.percent_arable
FROM arable_land a
WHERE a.country_name = 'World'
AND a.year BETWEEN 2000 AND 2019
),
fauna AS
(SELECT i.year,
SUM(i.species_count) AS animals_count
FROM iucn_species_count i
WHERE i.year BETWEEN 2000 AND 2019
GROUP BY i.year
),
flora AS
(SELECT p.assessment_year as year,
COUNT(*) AS plants_count
FROM plants_assessments p
WHERE p.assessment_year BETWEEN 2000 AND 2019
AND p.interpreted_status = 'Threatened'
GROUP BY p.assessment_year
),
ice AS
(SELECT s.date_year as year,
AVG(s.extent) FILTER(WHERE s.region = 'Arctic') AS arctic_sea_ice_extent,
AVG(s.extent) FILTER(WHERE s.region = 'Antarctica') AS antarctic_sea_ice_extent
FROM sea_ice_extent s
WHERE s.date_year BETWEEN 2000 AND 2019
GROUP BY s.date_year
),
ocean AS
(SELECT o.year as year,
AVG(o.tco2) AS total_co2,
AVG(o.phts25p0) AS ph_scale
FROM ocean_data o
WHERE o.year BETWEEN 2000 AND 2019
AND o.tco2 <> -9999 AND o.phts25p0 <> -9999
GROUP BY o.year
),
temp AS
(SELECT g.year as year,
AVG(g.global_mean) AS global_mean
FROM global_temperature g
WHERE g.year BETWEEN 2000 AND 2019
GROUP BY g.year
)
SELECT pop.year, pop.population, land.percent_arable, fauna.animals_count, flora.plants_count,
ice.arctic_sea_ice_extent, ice.antarctic_sea_ice_extent,
ocean.total_co2, ocean.ph_scale, temp.global_mean
FROM pop
JOIN land USING (year)
JOIN fauna USING (year)
JOIN flora USING (year)
JOIN ice USING (year)
JOIN ocean USING (year)
JOIN temp USING (year)
ORDER BY pop.year"
env_world_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(env_world_df)), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
year |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| 7 |
2008 |
6765986891 |
10.80640 |
16928 |
3279 |
10.97785 |
12.23941 |
2181.326 |
7.685694 |
0.5155556 |
| 8 |
2009 |
6849272706 |
10.77478 |
17291 |
2294 |
10.93197 |
12.04859 |
2176.104 |
7.735574 |
0.6294444 |
| 9 |
2010 |
6932596129 |
10.74432 |
18351 |
3217 |
10.71139 |
12.10679 |
2191.072 |
7.702093 |
0.7022222 |
| 10 |
2011 |
7014792135 |
10.80189 |
19570 |
2850 |
10.48350 |
11.50057 |
2224.690 |
7.675097 |
0.5844444 |
| 11 |
2012 |
7099311892 |
10.87358 |
20219 |
7556 |
10.40610 |
12.00444 |
2187.112 |
7.731700 |
0.6183333 |
| 12 |
2013 |
7184861447 |
10.89469 |
21353 |
5465 |
10.89712 |
12.52361 |
2183.400 |
7.723483 |
0.6438889 |
kable_styling(kable(cor(env_world_df[-1])), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| population |
1.0000000 |
0.1656147 |
0.9774118 |
0.7453433 |
-0.8026257 |
0.5047082 |
0.5832658 |
-0.1705909 |
0.5630321 |
| percent_arable |
0.1656147 |
1.0000000 |
0.2441270 |
0.4917138 |
0.0689147 |
0.3901610 |
-0.0431136 |
0.1567368 |
-0.3399306 |
| animals_count |
0.9774118 |
0.2441270 |
1.0000000 |
0.7450838 |
-0.8024194 |
0.5181381 |
0.6740957 |
-0.2912376 |
0.4783654 |
| plants_count |
0.7453433 |
0.4917138 |
0.7450838 |
1.0000000 |
-0.7224389 |
0.3576609 |
0.3673134 |
-0.1134181 |
0.3630865 |
| arctic_sea_ice_extent |
-0.8026257 |
0.0689147 |
-0.8024194 |
-0.7224389 |
1.0000000 |
-0.0588615 |
-0.7926131 |
0.5220906 |
-0.5461557 |
| antarctic_sea_ice_extent |
0.5047082 |
0.3901610 |
0.5181381 |
0.3576609 |
-0.0588615 |
1.0000000 |
0.1042700 |
0.0767117 |
0.0713994 |
| total_co2 |
0.5832658 |
-0.0431136 |
0.6740957 |
0.3673134 |
-0.7926131 |
0.1042700 |
1.0000000 |
-0.8165837 |
0.2653096 |
| ph_scale |
-0.1705909 |
0.1567368 |
-0.2912376 |
-0.1134181 |
0.5220906 |
0.0767117 |
-0.8165837 |
1.0000000 |
0.0463203 |
| global_mean |
0.5630321 |
-0.3399306 |
0.4783654 |
0.3630865 |
-0.5461557 |
0.0713994 |
0.2653096 |
0.0463203 |
1.0000000 |
par(mfrow=c(4,2), mai = c(0.3, 0.2, 0.7, 0.2))
for(x in colnames(env_world_df)[3:ncol(env_world_df)]) {
lfit <- loess(paste(x, "~ population"), data=env_world_df)
plot(as.formula(paste(x, "~ population")), env_world_df,
main=paste("population and\n", gsub("_", " ", x)), cex.main=2,
type="p", col=seaborn_palette[1], yaxt='n', xaxt='n', pch=19)
axis(side=1, at=axTicks(1), labels=format(axTicks(1), big.mark=',', scientific=FALSE))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE))
pop_order <- order(env_world_df$population)
lines(env_world_df$population[pop_order], lfit$fitted[pop_order], col=seaborn_palette[4], lwd=3)
}

dbDisconnect(conn)
[1] TRUE
User Data Application
# LINUX SHELL COMMAND CALL
system(paste0("gnome-terminal -- Rscript -e \"library(shiny); setwd('", getwd(), "'); runApp('EnvironmentDB_Shiny_App.R')\""))
Conclusion
Postgres as tool in data science workflow
Data persistence and hygiene
Centralization and security
Efficiency and usefulness
Scalability and applicability
LS0tCnRpdGxlOiAiUG9zdGdyZVNRTCBBcyBEYXRhIFNjaWVuY2UgRGF0YWJhc2UiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ci5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxMDAwcHg7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwp9Cjwvc3R5bGU+Cgo8YnIvPgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0Ij48aW1nIHNyYz0iSU1BR0VTL3Bvc3RncmVzcWxfci5wbmciIHdpZHRoPSIyMDBweCIvPjwvZGl2PgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChyb290LmRpciA9IGdldHdkKCkpCmBgYAoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShrYWJsZUV4dHJhKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHh0cykpCgpzZWFib3JuX3BhbGV0dGUgPC0gYygiIzRDNzJCMCIsICIjREQ4NDUyIiwgIiM1NUE4NjgiLCAiI0M0NEU1MiIsICIjODE3MkIzIiwgIiM5Mzc4NjAiLCAKICAgICAgICAgICAgICAgICAgICAgIiNEQThCQzMiLCAiIzhDOEM4QyIsICIjQ0NCOTc0IiwgIiM2NEI1Q0QiLCAiIzRDNzJCMCIsICIjREQ4NDUyIikKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9vcGVuX3NvdXJjZS5wbmciIHdpZHRoPSI3NXB4Ii8+PC9kaXY+Cjxici8+CgojIyBPcGVuIFNvdXJjZSBTdWNjZXNzIFN0b3JpZXMKCjxici8+Cgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9wb3N0Z3Jlc3FsLmpwZWciIHdpZHRoPSI1MHB4Ii8+PC9kaXY+Ci0gIyMjIFBvc3RncmVTUUw6IHBvd2VyZnVsLCBleHRlbnNpYmxlLCBhbmFseXRpY2FsIFJEQk1TCiAgICAtICMjIyMgW0NvbW11bml0eV0oaHR0cHM6Ly93d3cucG9zdGdyZXNxbC5vcmcvY29tbXVuaXR5Lyk6IGNvbnRyaWJ1dG9ycywgbWFpbGluZyBsaXN0cywgbG9jYWwgdXNlciBncm91cHMKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxpbWcgc3JjPSJJTUFHRVMvcl9sb2dvLnBuZyIgd2lkdGg9IjUwcHgiLz48L2Rpdj4KLSAjIyMgUjogZGF0YSBzY2llbmNlIHN0YWNrLCBleHRlbnNpYmxlIGVudmlyb25tZW50CiAgICAtICMjIyMgW0NSQU5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykgcmVwb3NpdG9yeSBvZiBwYWNrYWdlcwo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9weXRob24ucG5nIiB3aWR0aD0iNTBweCIvPjwvZGl2PgotICMjIyBQeXRob246IGdlbmVyYWwgcHVycG9zZSwgZXh0ZW5zaWJsZSBsYW5ndWFnZQogICAgLSAjIyMjIFtQeVBJXShodHRwczovL3B5cGkub3JnLykgZWNvc3lzdGVtIG9mIG1vZHVsZXMKCjxkaXYgc3R5bGU9Im1hcmdpbi1sZWZ0OjQwcHg7Ii8+PGgzPkNoYWxsZW5naW5nIENvbW1lcmNpYWwgTWFya2V0IFNoYXJlPC9oMz48L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDsgbWFyZ2luLWxlZnQ6MTAwcHg7Ii8+PGltZyBzcmM9IklNQUdFUy9jb21tZXJjaWFsX3Rvb2xzLnBuZyIgd2lkdGg9IjIwMHB4Ii8+PC9kaXY+Cjxici8+Cjxici8+Cjxici8+Cjxici8+Cgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+Cgo8YnIvPgoKPGgyPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlJlbGF0aW9uYWwgRGF0YWJhc2UgU29sdXRpb248L3NwYW4+PC9oMj4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0OyI+PGltZyBzcmM9IklNQUdFUy9SREJNU19Hcm91cC5wbmciIHdpZHRoPSIyNTBweCIvPjwvZGl2PgoKLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+RGF0YSBwZXJzaXN0ZW5jZTogaGlzdG9yaWNhbCBhbmQgY3VycmVudCBuZWVkczwvc3Bhbj4KLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+TWFpbnRlbmFuY2UvaHlnaWVuZTogYWRoZXJlbmNlIHRvIHR5cGVzPC9zcGFuPgoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQ7Ij48aW1nIHNyYz0iSU1BR0VTL3Bvc3RncmVzcWwuanBlZyIgd2lkdGg9IjEwMHB4Ii8+PC9kaXY+CgotICMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5TdG9yYWdlIGVmZmljaWVuY3k6IG5vcm1hbGl6YXRpb24gcmVkdWNlcyByZXBldGl0aW9uIG9mIGRhdGE8L3NwYW4+Ci0gIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNlbnRyYWxpemF0aW9uOiBtdWx0aXBsZSB1c2VyIGVudmlyb25tZW50IGFuZCBzZWN1cml0eTwvc3Bhbj4KLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+U2NhbGFiaWxpdHk6IG5vdCBsaW1pdGVkIHRvIGxvY2FsIHJlc291cmNlczwvc3Bhbj4KCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL2RvY2tlcl9wZ3NxbC5wbmciIHdpZHRoPSIxNTBweCIvPjwvZGl2PgojIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5Vc2UgQ2FzZTogRW52aXJvbm1lbnQgRGF0YWJhc2U8L2E+PC9zcGFuPgoKPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxpPkh1bWFuIEltcGFjdCBvbiB0aGUgR2xvYmFsIGFuZCBMb2NhbCBCaW9zcGhlcmUgYW5kIENsaW1hdGU8L2k+PC9zcGFuPjwvaDM+CjxkaXYgc3R5bGU9ImZsb2F0OmNlbnRlcjsgdGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iSU1BR0VTL2Vudl9kYXRhLnBuZyIvPjwvZGl2Pgo8YnIvPgoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKIyMgQ29ubmVjdGlvbgoKLSAjIyMgTm8gZmlsZSBzeXN0ZW0gb2YgZm9sZGVycyBhbmQgc3ViZm9sZGVycwotICMjIyBObyBtdWx0aXBsZSBmbGF0IGZpbGVzCi0gIyMjIENlbnRyYWxpemVkIGxvY2F0aW9uIGZvciBtdWx0aXBsZSB1c2VycwoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnBvc3RncmVzcWwub3JnL2Z0cC9vZGJjL3ZlcnNpb25zL3NyYy8iIHRhcmdldD0iYmxhbmsiPk9EQkM8L2E+PC9zcGFuPgoKYGBge3J9CmxpYnJhcnkoREJJKQpsaWJyYXJ5KG9kYmMpCgpjb25uIDwtIGRiQ29ubmVjdChvZGJjOjpvZGJjKCksIGRyaXZlcj0iUG9zdGdyZVNRTCBVbmljb2RlIiwgCiAgICAgICAgICAgICAgICAgIHNlcnZlcj0ibG9jYWxob3N0IiwgZGF0YWJhc2U9ImVudmlyb25tZW50IiwKICAgICAgICAgICAgICAgICAgdWlkPSJwb3N0Z3JlcyIsIHB3ZD0iZW52MTkiLCBwb3J0PTY0MzIpCmRiTGlzdFRhYmxlcyhjb25uKQoKZGJEaXNjb25uZWN0KGNvbm4pCmBgYAoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vamRiYy5wb3N0Z3Jlc3FsLm9yZy9kb3dubG9hZC5odG1sIiB0YXJnZXQ9ImJsYW5rIj5KREJDPC9hPjwvc3Bhbj4KCmBgYHtyfQpsaWJyYXJ5KFJKREJDKQoKZHJ2IDwtIEpEQkMoIm9yZy5wb3N0Z3Jlc3FsLkRyaXZlciIsCiAgICAgICAgICAgIi91c3IvbGliL2p2bS9qYXZhLTgtb3JhY2xlL2xpYi9wb3N0Z3Jlc3FsLTQyLjIuMi5qYXIiKQpjb25uIDwtIGRiQ29ubmVjdChkcnYsICJqZGJjOnBvc3RncmVzcWw6Ly9sb2NhbGhvc3Q6NjQzMi9lbnZpcm9ubWVudCIsICJwb3N0Z3JlcyIsICJlbnYxOSIpCmRiTGlzdFRhYmxlcyhjb25uLCBzY2hlbWE9InB1YmxpYyIpCgpkYkRpc2Nvbm5lY3QoY29ubikKYGBgCgoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPjxhIGhyZWY9Imh0dHBzOi8vcmRyci5pby9jcmFuL1JQb3N0Z3JlU1FMLyIgdGFyZ2V0PSJibGFuayI+Ui1Qb3N0Z3JlcyBBUEk8L2E+PC9zcGFuPgoKYGBge3J9CmxpYnJhcnkoUlBvc3RncmVTUUwpCgpjb25uIDwtIGRiQ29ubmVjdChSUG9zdGdyZVNRTDo6UG9zdGdyZVNRTCgpLCBob3N0PSJsb2NhbGhvc3QiLCBkYm5hbWU9ImVudmlyb25tZW50IiwKICAgICAgICAgICAgICAgICAgdXNlcj0icG9zdGdyZXMiLCBwYXNzd29yZD0iZW52MTkiLCBwb3J0PTY0MzIpCmRiTGlzdFRhYmxlcyhjb25uKQpgYGAKCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCiMjIFByb2Nlc3MgRGF0YQoKLSAjIyMjIFByb2FjdGl2ZWx5IGRlZmluZSBkYXRhIHR5cGVzIGFuZCBzY2FsZQotICMjIyMgU2VhbWxlc3MgYnVsayB0ZXh0IGZpbGUgaW1wb3J0Ci0gIyMjIyBBbm5vdGF0ZSBzb3VyY2VzIHdpdGggY29tbWVudHMKCmBgYHtzcWwsIGV2YWw9RkFMU0V9CkNSRUFURSBUQUJMRSBnbG9iYWxfdGVtcGVyYXR1cmUgKAogICBpZCBTRVJJQUwgTk9UIE5VTEwgUFJJTUFSWSBLRVksCiAgIHllYXIgSU5ULAogICBwZXJpb2QgVkFSQ0hBUig1MCksCiAgIGdsb2JhbF9tZWFuIE5VTUVSSUMoNSwyKQopOwoKXGNvcHkgZ2xvYmFsX3RlbXBlcmF0dXJlICh5ZWFyLCBwZXJpb2QsIGdsb2JhbF9tZWFuKSBGUk9NICcvaG9tZS9wYXJmYWl0Zy9EYXRhYmFzZXMvU1FMX1NlcnZlci9FTlZJUk9OTUVOVC9nbG9iYWxfdGVtcGVyYXR1cmUuY3N2JyBERUxJTUlURVIgJywnIENTViBIRUFERVI7CgpDT01NRU5UIE9OIFRBQkxFIGdsb2JhbF90ZW1wZXJhdHVyZSBJUyAnU291cmNlOiBOQVNBIC0gQ29tYmluZWQgTGFuZC1TdXJmYWNlIEFpciBhbmQgU2VhLVN1cmZhY2UgV2F0ZXIgVGVtcGVyYXR1cmUgQW5vbWFsaWVzIChMYW5kLU9jZWFuIFRlbXBlcmF0dXJlIEluZGV4LCBMT1RJKSAoaHR0cHM6Ly9kYXRhLmdpc3MubmFzYS5nb3YvZ2lzdGVtcC8pJzsKYGBgCgpgYGB7c3FsIGNvbm5lY3Rpb249Y29ubiwgb3V0cHV0LnZhciA9ICJjb21tZW50c19kZiJ9ClNFTEVDVCByZWxuYW1lIGFzIHRhYmxlLCBvYmpfZGVzY3JpcHRpb24ob2lkKSBhcyBjb21tZW50CkZST00gcGdfY2xhc3MKV0hFUkUgcmVsa2luZCA9ICdyJwogIEFORCBvYmpfZGVzY3JpcHRpb24ob2lkKSBJUyBOT1QgTlVMTAogIE9SREVSIEJZIHJlbG5hbWUKYGBgCgpgYGB7cn0Ka2FibGVfc3R5bGluZyhrYWJsZShjb21tZW50c19kZiksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKIyMgQW5hbHl6ZSBEYXRhCgotICMjIyMgU1FMIGpvaW5zIGRpc3BhcmF0ZSBkYXRhc2V0cwotICMjIyMgUXVlcmllcyBleHBvc2UgcmVsYXRpb25zaGlwCi0gIyMjIyBBZGp1c3QgY29sdW1ucyBmb3IgdGFibGUvZ3JhcGggbmVlZHMKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48YSBocmVmPSJodHRwczovL3d3dy5lc3JsLm5vYWEuZ292L2dtZC9vYm9wL21sby8iIGJsYW5rPSJ0YXJnZXQiPjxpbWcgc3JjPSJJTUFHRVMvbm9hYS5wbmciIGhlaWdodD0iNTBweCIvPjwvYT48L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vd3d3LmVpYS5nb3YvdG90YWxlbmVyZ3kvZGF0YS9tb250aGx5LyIgYmxhbmsgPSJ0YWdldCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgaGVpZ2h0PSI1MHB4Ii8+PC9hPjwvZGl2PgoKCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5EZXBhcnRtZW50IG9mIEVuZXJneSdzIEVJQTogTW9udGhseSBFbmVyZ3kgUmV2aWV3ICY8L3NwYW4+CiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5OT0FBIE1hdW5hIExvYSwgSGF3YWlpIE9ic2VydmF0b3J5IC0gQ2FyYm9uIFBQTTwvc3Bhbj4KCmBgYHtzcWwgY29ubmVjdGlvbj1jb25uLCBvdXRwdXQudmFyID0gIm1ldHJpY3NfZGYifQpTRUxFQ1QgcC5kYXRlX3llYXIsIHAuZGF0ZV9tb250aCwgQ09OQ0FUKHAuZGF0ZV95ZWFyLCAnLScsIHAuZGF0ZV9tb250aCwgJy0wMScpOjpkYXRlIGFzIGRhdGVfZGF5LAogICAgICAgICAgICAgICBwLmF2ZXJhZ2VfcHBtIGFzICJjYXJib24gcHBtIiwgYy5lbmVyZ3lfY29uc3VtZWQgYXMgImJ0dSBjb25zdW1lZCIsIGUuZW5lcmd5X2NvMiBhcyAiY28yIGVtaXNzaW9ucyIKICBGUk9NIHBwbV9tb250aCBwCiAgSk9JTiBjb25zdW1wdGlvbiBjIAogICAgT04gcC5kYXRlX3llYXIgPSBjLmRhdGVfeWVhciBBTkQgcC5kYXRlX21vbnRoID0gYy5kYXRlX21vbnRoIGFuZCBjLm1zbiA9ICdUWFJDQlVTJwogIEpPSU4gdXNfY28yX2VtaXNzaW9ucyBlCiAgICBPTiBwLmRhdGVfeWVhciA9IGUuZGF0ZV95ZWFyIEFORCBwLmRhdGVfbW9udGggPSBlLmRhdGVfbW9udGggYW5kIGUubXNuID0gJ1RFVENFVVMnCiAgT1JERVIgQlkgcC5kYXRlX3llYXIsIHAuZGF0ZV9tb250aApgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1jb25uLCBvdXRwdXQudmFyID0gImFnZ19kZiJ9ClNFTEVDVCBwLmRhdGVfeWVhciwKICAgICAgIHN1bShwLmF2ZXJhZ2VfcHBtKSBhcyBjYXJib25fcHBtX3RvdGFsLCAKICAgICAgIGF2ZyhwLmF2ZXJhZ2VfcHBtKSBhcyBjYXJib25fcHBtX21lYW4sIAogICAgICAgc3VtKGMuZW5lcmd5X2NvbnN1bWVkKSBhcyBidHVfY29uc3VtZWRfdG90YWwsCiAgICAgICBhdmcoYy5lbmVyZ3lfY29uc3VtZWQpIGFzIGJ0dV9jb25zdW1lZF9tZWFuLAogICAgICAgc3VtKGUuZW5lcmd5X2NvMikgYXMgY28yX2VtaXNzaW9uc190b3RhbCwKICAgICAgIGF2ZyhlLmVuZXJneV9jbzIpIGFzIGNvMl9lbWlzc2lvbnNfbWVhbgogIEZST00gcHBtX21vbnRoIHAKICBKT0lOIGNvbnN1bXB0aW9uIGMgCiAgICBPTiBwLmRhdGVfeWVhciA9IGMuZGF0ZV95ZWFyIEFORCBwLmRhdGVfbW9udGggPSBjLmRhdGVfbW9udGggYW5kIGMubXNuID0gJ1RYUkNCVVMnCiAgSk9JTiB1c19jbzJfZW1pc3Npb25zIGUKICAgIE9OIHAuZGF0ZV95ZWFyID0gZS5kYXRlX3llYXIgQU5EIHAuZGF0ZV9tb250aCA9IGUuZGF0ZV9tb250aCBhbmQgZS5tc24gPSAnVEVUQ0VVUycKR1JPVVAgQlkgcC5kYXRlX3llYXIKT1JERVIgQlkgcC5kYXRlX3llYXIKYGBgCgpgYGB7cn0Ka2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGFnZ19kZiwgMTApKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBmaWcxYSwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEzLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KbWV0cmljX3RzIDwtIHh0cyh4PW1ldHJpY3NfZGZbYygiY2FyYm9uIHBwbSIsICJidHUgY29uc3VtZWQiLCAiY28yIGVtaXNzaW9ucyIpXSwgCiAgICAgICAgICAgICAgICAgb3JkZXIuYnk9bWV0cmljc19kZiRkYXRlX2RheSkKCnBsb3QobWV0cmljX3RzLCBtYWluID0gIkVuZXJneSBhbmQgQ2FyYm9uIFBQTSBNZXRyaWNzIiwKICAgICBsZWdlbmQubG9jPSJib3R0b21yaWdodCIsIAogICAgIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxOjNdLAogICAgIHlheGlzLnJpZ2h0PUZBTFNFLAogICAgIGF4ZXM9RkFMU0UsCiAgICAgbHdkPTEsCiAgICAgY2V4Lm1haW49MywKICAgICBtYWpvci50aWNrcz0ieWVhcnMiLAogICAgIG1ham9yLmZvcm1hdD0iJVkiLAogICAgIG1pbm9yLmZvcm1hdD0iJVkiLAogICAgIGdyaWQudGlja3MubHR5PTEpCmBgYAoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlNlYXNvbmFsIERlY29tcG9zaXRpb248L3NwYW4+CgpgYGB7ciAgZmlnMWIsIGZpZy5oZWlnaHQgPSA3LCBmaWcud2lkdGggPSAxMywgZmlnLmFsaWduID0gImNlbnRlciJ9CmNhcmJvbnBwbV90cyA8LSB0cyhtZXRyaWNzX2RmJGBjYXJib24gcHBtYCwgc3RhcnQ9YygxOTczLCAxKSwgZnJlcXVlbmN5PTEyKQpjYXJib25wcG1fc3RsIDwtIHN0bChjYXJib25wcG1fdHMsIHMud2luZG93PSJwZXJpb2RpYyIpCgpwbG90KGNhcmJvbnBwbV9zdGwsIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxXSwgCiAgICAgbWFpbj0iU2Vhc29uYWwgRGVjb21wb3NpdGlvbiBvZiBHbG9iYWwgQ2FyYm9uIFBQTSIpCmBgYAoKYGBge3IgIGZpZzFjLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTMsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpjb25zdW1lZF90cyA8LSB0cyhtZXRyaWNzX2RmJGBidHUgY29uc3VtZWRgLCBzdGFydD1jKDE5NzMsIDEpLCBmcmVxdWVuY3k9MTIpCmNvbnN1bWVkX3N0bCA8LSBzdGwoY29uc3VtZWRfdHMsIHMud2luZG93PSJwZXJpb2RpYyIpCgpwbG90KGNvbnN1bWVkX3N0bCwgY29sID0gc2VhYm9ybl9wYWxldHRlWzJdLCAKICAgICBtYWluPSJTZWFzb25hbCBEZWNvbXBvc2l0aW9uIG9mIFUuUy4gRW5lcmd5IENvbnN1bXB0aW9uIikKYGBgCgpgYGB7ciBmaWcxZCwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEzLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KZW1pc3Npb25zX3RzIDwtIHRzKG1ldHJpY3NfZGYkYGNvMiBlbWlzc2lvbnNgLCBzdGFydD1jKDE5NzMsIDEpLCBmcmVxdWVuY3k9MTIpCmVtaXNzaW9uc19zdGwgPC0gc3RsKGVtaXNzaW9uc190cywgcy53aW5kb3c9InBlcmlvZGljIikKCnBsb3QoZW1pc3Npb25zX3N0bCwgY29sID0gc2VhYm9ybl9wYWxldHRlWzNdLAogICAgIG1haW4gPSAiU2Vhc29uYWwgRGVjb21wb3NpdGlvbiBvZiBVLlMuIENPMiBFbWlzc2lvbnMiKQpgYGAKCmBgYHtyfQpzcWwgPC0gIldJVEggYzEgQVMKICAgICAgICAgICAoU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZGVzY3JpcHRpb24sICdQcmltYXJ5IEVuZXJneSBDb25zdW1lZCBieSB0aGUgJywgJycpIEFTIFwiU2VjdG9yXCIsCiAgICAgICAgICAgICAgICAgICBTVU0oZW5lcmd5X2NvbnN1bWVkKSBBUyBcIlByaW1hcnkgRW5lcmd5IENvbnN1bWVkXCIKICAgICAgICAgICAgRlJPTSBjb25zdW1wdGlvbgogICAgICAgICAgICBXSEVSRSBtc24gSU4gKCdUWElDQlVTJywgJ1RYUkNCVVMnLCAnVFhBQ0JVUycsICdUWENDQlVTJywgJ1RYRUlCVVMnKQogICAgICAgICAgICBHUk9VUCBCWSBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpLCBkZXNjcmlwdGlvbgogICAgICAgICAgICksIGMyIEFTCiAgICAgICAgICAgKFNFTEVDVCBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIGFzIGRlY2FkZSwgCiAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRlc2NyaXB0aW9uLCAnVG90YWwgRW5lcmd5IENvbnN1bWVkIGJ5IHRoZSAnLCAnJykgQVMgXCJTZWN0b3JcIiwKICAgICAgICAgICAgICAgICAgIFNVTShlbmVyZ3lfY29uc3VtZWQpIEFTIFwiVG90YWwgRW5lcmd5IENvbnN1bWVkXCIKICAgICAgICAgICAgRlJPTSBjb25zdW1wdGlvbgogICAgICAgICAgICBXSEVSRSBtc24gSU4gKCdURUNDQlVTJywgJ1RFQUNCVVMnLCAnVEVJQ0JVUycsICdURVJDQlVTJykKICAgICAgICAgICAgR1JPVVAgQlkgQ09OQ0FUKChkYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSwgZGVzY3JpcHRpb24KICAgICAgICAgICApCgogICAgICAgIFNFTEVDVCBjMS5kZWNhZGUsIGMxLlwiU2VjdG9yXCIsIGMxLlwiUHJpbWFyeSBFbmVyZ3kgQ29uc3VtZWRcIiwgYzIuXCJUb3RhbCBFbmVyZ3kgQ29uc3VtZWRcIgogICAgICAgIEZST00gYzEKICAgICAgICBMRUZUIEpPSU4gYzIKICAgICAgICAgICAgT04gYzEuXCJTZWN0b3JcIiA9IGMyLlwiU2VjdG9yXCIgQU5EIGMxLmRlY2FkZSA9IGMyLmRlY2FkZQogICAgICAgIE9SREVSIEJZIGMxLmRlY2FkZSwgYzEuXCJTZWN0b3JcIgogICAgICAgIgoKY29uc3VtZWRfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoY29uc3VtZWRfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNvbnN1bXB0aW9uIGFuZCBDTzIgRW1pc3Npb25zPC9zcGFuPgoKCmBgYHtyIGZpZzFlLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKHN1YnNldChjb25zdW1lZF9kZiwgU2VjdG9yICE9ICdFbGVjdHJpYyBQb3dlciBTZWN0b3InKSwKICAgICAgICAgICAgICAgICB0YXBwbHkoYFRvdGFsIEVuZXJneSBDb25zdW1lZGAsIGxpc3QoZGVjYWRlLCBgU2VjdG9yYCksIHN1bSkpCgpwYXIobWFyPWMoNSwgNSwgMiwgMSkpCmJhcnBsb3QocGxvdF9tYXQsIG1haW49IlUuUy4gVG90YWwgRW5lcmd5IENvbnN1bXB0aW9uIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6OF0sIHlsaW09YygwLCA4RTUpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoNSwgMTQsIDIzLCAzMiksIGxhYmVscz1jb2xuYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjhdLCBuY29sPTgpCmBgYAoKYGBge3IgZmlnMWYsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBsb3RfbWF0IDwtIHdpdGgoY29uc3VtZWRfZGYsIHRhcHBseShgUHJpbWFyeSBFbmVyZ3kgQ29uc3VtZWRgLCBsaXN0KGRlY2FkZSwgYFNlY3RvcmApLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIFByaW1hcnkgRW5lcmd5IENvbnN1bXB0aW9uIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6OF0sIHlsaW09YygwLCA5RTUpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoNSwgMTQsIDIzLCAzMiwgNDEpLCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo4XSwgbmNvbD04KQpgYGAKCmBgYHtyfQpzcWwgPC0gIlNFTEVDVCBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIGFzIGRlY2FkZSwgCiAgICAgICAgICAgICAgIFJFUExBQ0UoUkVQTEFDRShkZXNjcmlwdGlvbiwgJ1RvdGFsIEVuZXJneSAnLCAnJyksICcgQ08yIEVtaXNzaW9ucycsICcnKSBBUyBcIlNlY3RvclwiLAogICAgICAgICAgICAgICBTVU0oZW5lcmd5X2NvMikgQVMgXCJUb3RhbCBDTzIgRW1pc3Npb25zXCIKICAgICAgICBGUk9NIHVzX2NvMl9lbWlzc2lvbnMKICAgICAgICBXSEVSRSBtc24gSU4gKCdURVJDRVVTJywgJ1RFQ0NFVVMnLCAnVEVBQ0VVUycsICdUWEVJRVVTJykKICAgICAgICBHUk9VUCBCWSBkYXRlX3llYXIsIGRlc2NyaXB0aW9uCiAgICAgICAgT1JERVIgQlkgZGF0ZV95ZWFyLCBkZXNjcmlwdGlvbgogICAgICAgIgoKZW1pc3Npb25zX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGVtaXNzaW9uc19kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzFnLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGVtaXNzaW9uc19kZiwgdGFwcGx5KGBUb3RhbCBDTzIgRW1pc3Npb25zYCwgbGlzdChkZWNhZGUsIGBTZWN0b3JgKSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA4LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBDTzIgRW1pc3Npb25zIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCA2RTQpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoMywgOSwgMTUsIDIxKSswLjUsIGxhYmVscz1jb2xuYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKCmBgYHtyfQpzcWwgPC0gIldJVEggc3ViIEFTCiAgICAgICAgICAoU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgICAgUkVQTEFDRSgkMSwgJyUnLCAnJykgYXMgc2VjdG9yLAogICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRlc2NyaXB0aW9uLCAnQ29tbWVyY2lhbCBTZWN0b3IgQ08yIEVtaXNzaW9ucycsICcnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVzaWRlbnRpYWwgU2VjdG9yIENPMiBFbWlzc2lvbnMnLCAnJwogICAgICAgICAgICAgICAgICAgICAgICksICdUcmFuc3BvcnRhdGlvbiBTZWN0b3IgQ08yIEVtaXNzaW9ucycsICcnKSwKICAgICAgICAgICAgICAgICAgJyAnLCAnXG4nKSAgQVMgXCJUeXBlXCIsCiAgICAgICAgICAgICAgICAgZW5lcmd5X2NvMgogICAgICAgICAgIEZST00gdXNfY28yX2VtaXNzaW9ucwogICAgICAgICAgIFdIRVJFIGRlc2NyaXB0aW9uIExJS0UgJDIpCgogICAgICAgU0VMRUNUIGRlY2FkZSwgc2VjdG9yLCBcIlR5cGVcIiwgCiAgICAgICAgICAgICAgU1VNKGVuZXJneV9jbzIpIEFTIFwiVG90YWwgQ08yIEVtaXNzaW9uc1wiCiAgICAgICBGUk9NIHN1YgogICAgICAgR1JPVVAgQlkgZGVjYWRlLCBzZWN0b3IsIFwiVHlwZVwiCiAgICAgICBPUkRFUiBCWSBkZWNhZGUsIHNlY3RvciwgXCJUeXBlXCIKICAgICAgICIKCnBhcmFtcyA8LSBwYXN0ZTAoYygiJVRyYW5zcG9ydGF0aW9uIiwgIiVSZXNpZGVudGlhbCIsICIlQ29tbWVyY2lhbCIpLCAiIFNlY3RvciUiKQplbWlzc2lvbnNfdHlwZV9kZiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkocGFyYW1zLCBmdW5jdGlvbihwKSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCwgcGFyYW09bGlzdChwLCBwKSkpKQpgYGAKCmBgYHtyIGZpZzFoLCBmaWcuaGVpZ2h0ID0gMTIsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KCnBhcihtZnJvdz1jKDMsMiksIG1hcj1jKDUsIDUsIDIsIDEpLCBtYWkgPSBjKDAuNywgMC4yLCAwLjcsIDAuMikpCm91dHB1dCA8LSBieShlbWlzc2lvbnNfdHlwZV9kZiwgZW1pc3Npb25zX3R5cGVfZGYkc2VjdG9yLCBmdW5jdGlvbihzdWIpIHsKICBwbG90X21hdCA8LSB3aXRoKHN1YiwgdGFwcGx5KGBUb3RhbCBDTzIgRW1pc3Npb25zYCwgbGlzdChkZWNhZGUsIGBUeXBlYCksIHN1bSkpCgogIGJhcnBsb3QocGxvdF9tYXQsIG1haW49cGFzdGUoIlUuUy4gQ08yIEVtaXNzaW9ucyBieSIsIHN1YiRzZWN0b3JbWzFdXSksIGNleC5tYWluPTEuNSwKICAgICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgeWxpbT1jKDAsIGNlaWxpbmcobWF4KHBsb3RfbWF0LCBuYS5ybT1UUlVFKS8xRTQpICogMUU0KSwgCiAgICAgICAgICB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQogIAogIGF4aXMoc2lkZT0xLCBhdD1jKDMsOSwxNSwyMSwyNywzMywzOSw0NSw1MSw1Nyw2Mylbc2VxX2Fsb25nKGNvbG5hbWVzKHBsb3RfbWF0KSldICsgMC41LAogICAgICAgbGFiZWxzPWNvbG5hbWVzKHBsb3RfbWF0KSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCiAgYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCiAgbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCn0pCmBgYAoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxpbWcgc3JjPSJJTUFHRVMvZG9lX2VpYS5wbmciIHdpZHRoPSI3NXB4Ii8+PC9kaXY+CiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5SZW5ld2FibGUgRW5lcmd5OiBQcm9kdWN0aW9uIGFuZCBDb25zdW1wdGlvbjwvc3Bhbj4KCmBgYHtyfQpzcWwgPC0gIlNFTEVDVCBlbmVyZ3lfdHlwZSwKICAgICAgICAgICAgICAgZGF0ZSwKICAgICAgICAgICAgICAgU1VNKHByb2R1Y3Rpb24pIEFTIHByb2R1Y3Rpb24sCiAgICAgICAgICAgICAgIFNVTShjb25zdW1wdGlvbikgQVMgY29uc3VtcHRpb24KICAgICAgICBGUk9NIHVzX3JlbmV3YWJsZV9lbmVyZ3kKICAgICAgICBHUk9VUCBCWSBlbmVyZ3lfdHlwZSwKICAgICAgICAgICAgICAgICBkYXRlCiAgICAgICAgT1JERVIgQlkgZW5lcmd5X3R5cGUsCiAgICAgICAgICAgICAgICAgZGF0ZQogICAgICAgIgoKcmVuZXdhYmxlX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKHJlbmV3YWJsZV9kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIHJlbmV3YWJsZV9maWcsIGZpZy5oZWlnaHQgPSAxNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKcGFyKG1mcm93PWMoMywzKSwgbWFyPWMoNSwgNSwgMiwgMSkpCm91dHB1dCA8LSBieShyZW5ld2FibGVfZGYsIHJlbmV3YWJsZV9kZiRlbmVyZ3lfdHlwZSwgZnVuY3Rpb24oc3ViKSB7CiAgCiAgbWV0cmljX3RzIDwtIHh0cyh4PXN1YltjKCJwcm9kdWN0aW9uIiwgImNvbnN1bXB0aW9uIildLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyLmJ5PXN1YiRkYXRlKQogIAogIHByaW50KHBsb3QobWV0cmljX3RzLCBtYWluID0gc3ViJGVuZXJneV90eXBlWzFdLAogICAgICAgICAgICAgbGVnZW5kLmxvYz0iYm90dG9tcmlnaHQiLCAKICAgICAgICAgICAgIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxOjNdLAogICAgICAgICAgICAgeWF4aXMucmlnaHQ9RkFMU0UsCiAgICAgICAgICAgICBheGVzPUZBTFNFLAogICAgICAgICAgICAgbHdkPTEsCiAgICAgICAgICAgICBjZXgubWFpbj0zLAogICAgICAgICAgICAgbWFqb3IudGlja3M9InllYXJzIiwKICAgICAgICAgICAgIG1ham9yLmZvcm1hdD0iJVkiLAogICAgICAgICAgICAgbWlub3IuZm9ybWF0PSIlWSIsCiAgICAgICAgICAgICBncmlkLnRpY2tzLmx0eT0xKSkKICAKfSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93YXRlcmRhdGEudXNncy5nb3Yvbndpcy9ndyI+PGltZyBzcmM9IklNQUdFUy91c2dzLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2E+PC9kaXY+CgojIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+VS5TLiBHZW9sb2dpY2FsIFN1cnZleSAtIEdyb3VuZHdhdGVyIFdlbGwgRGVwdGggRGF0YTwvc3Bhbj4KYGBge3J9CnNxbCA8LSAiU0VMRUNUIGcueWVhciwgZy5tb250aCwKICAgICAgICAgICAgICAgYXZnKGcubWVhbl92YWx1ZSkgYXMgbWVhbl92YWx1ZQogICAgICAgIEZST00gZ3JvdW5kd2F0ZXIgZwogICAgICAgIFdIRVJFIGcueWVhciBCRVRXRUVOIDE5OTAgQU5EIDIwMTkKICAgICAgICBHUk9VUCBCWSBnLnllYXIsIGcubW9udGgiCgpncm91bmR3YXRlcl9kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChncm91bmR3YXRlcl9kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzMsIGZpZy5oZWlnaHQgPSA3LCBmaWcud2lkdGggPSAxNiwgZmlnLmFsaWduID0gImNlbnRlciJ9CmJveHBsb3QobWVhbl92YWx1ZSB+IHllYXIsIGdyb3VuZHdhdGVyX2RmLCBjb2w9c2VhYm9ybl9wYWxldHRlWzE6MTBdLCAKICAgICAgICBtYWluPSJHcm91bmR3YXRlciBXZWxsIERlcHRoIE1lYW4gVmFsdWVzIiwgY2V4Lm1haW49MS41KQpgYGAKCgpgYGB7cn0Kc3FsIDwtICJXSVRIIHNpdGVzIEFTICgKICAgICAgICAgICAgU0VMRUNUIENPTkNBVChnLnllYXIsICctJywgZy5tb250aCwgJy0xJyk6OmRhdGUgQVMgXCJkYXRlXCIsCiAgICAgICAgICAgICAgICAgICBnLnNpdGVfbmFtZSwKICAgICAgICAgICAgICAgICAgIEFWRyhnLm1lYW5fdmFsdWUpIGFzIG1lYW5fdmFsdWUKICAgICAgICAgICAgRlJPTSBncm91bmR3YXRlciBnCiAgICAgICAgICAgIFdIRVJFIGcueWVhciBCRVRXRUVOIDE5OTAgQU5EIDIwMTkKICAgICAgICAgICAgR1JPVVAgQlkgQ09OQ0FUKGcueWVhciwgJy0nLCBnLm1vbnRoLCAnLTEnKTo6ZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgZy5zaXRlX25hbWUKICAgICAgICApLCBib3R0b20gQVMgKAogICAgICAgICAgICBTRUxFQ1Qgc2l0ZV9uYW1lLCBBVkcobWVhbl92YWx1ZSkgQVMgbWVhbl92YWx1ZSwgJ2JvdHRvbV9zaXRlcycgQVMgY2F0ZWdvcnkKICAgICAgICAgICAgRlJPTSBzaXRlcyAKICAgICAgICAgICAgR1JPVVAgQlkgc2l0ZV9uYW1lCiAgICAgICAgICAgIE9SREVSIEJZIEFWRyhtZWFuX3ZhbHVlKSBBU0MgTElNSVQgNQogICAgICAgICksIHRvcCBBUyAoCiAgICAgICAgICAgIFNFTEVDVCBzaXRlX25hbWUsIEFWRyhtZWFuX3ZhbHVlKSBBUyBtZWFuX3ZhbHVlLCAndG9wX3NpdGVzJyBBUyBjYXRlZ29yeQogICAgICAgICAgICBGUk9NIHNpdGVzIAogICAgICAgICAgICBHUk9VUCBCWSBzaXRlX25hbWUKICAgICAgICAgICAgT1JERVIgQlkgQVZHKG1lYW5fdmFsdWUpIERFU0MgTElNSVQgNQogICAgICAgICkKCiAgICAgICAgU0VMRUNUIHNpdGVzLlwiZGF0ZVwiLCBzaXRlcy5tZWFuX3ZhbHVlLCBzaXRlcy5zaXRlX25hbWUsIHRvcC5jYXRlZ29yeQogICAgICAgIEZST00gc2l0ZXMKICAgICAgICBJTk5FUiBKT0lOIHRvcAogICAgICAgICAgICBPTiBzaXRlcy5zaXRlX25hbWUgPSB0b3Auc2l0ZV9uYW1lCgogICAgICAgIFVOSU9OCgogICAgICAgIFNFTEVDVCBzaXRlcy5cImRhdGVcIiwgc2l0ZXMubWVhbl92YWx1ZSwgc2l0ZXMuc2l0ZV9uYW1lLCBib3R0b20uY2F0ZWdvcnkKICAgICAgICBGUk9NIHNpdGVzCiAgICAgICAgSU5ORVIgSk9JTiBib3R0b20KICAgICAgICAgICAgT04gc2l0ZXMuc2l0ZV9uYW1lID0gYm90dG9tLnNpdGVfbmFtZQoKICAgICAgICBPUkRFUiBCWSBjYXRlZ29yeSwgc2l0ZV9uYW1lLCBcImRhdGVcIiIKCmdyb3VuZHdhdGVyX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZShoZWFkKGdyb3VuZHdhdGVyX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZ3JvdW5kd2F0ZXJfZmlnLCBmaWcuaGVpZ2h0ID0gMjUsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KCmdyb3VuZHdhdGVyX2RmJHllYXIgPC0gZm9ybWF0KGdyb3VuZHdhdGVyX2RmJGRhdGUsICIlWSIpCgpwYXIobWZyb3c9Yyg1LDIpLCBtYXI9Yyg1LCA1LCAyLCAxKSkKb3V0cHV0IDwtIGJ5KGdyb3VuZHdhdGVyX2RmLCBncm91bmR3YXRlcl9kZiRzaXRlX25hbWUsIGZ1bmN0aW9uKHN1YikgewogIAogIG1ldHJpY190cyA8LSB4dHMoeD1zdWJbYygibWVhbl92YWx1ZSIpXSwgCiAgICAgICAgICAgICAgICAgICBvcmRlci5ieT1zdWIkZGF0ZSkKICAKICBwcmludChwbG90KG1ldHJpY190cywgbWFpbiA9IHBhc3RlKHN1YiRzaXRlX25hbWVbMV0sICc6Jywgc3ViJGNhdGVnb3J5WzFdKSwKICAgICAgICAgICAgIGxlZ2VuZC5sb2M9ImJvdHRvbXJpZ2h0IiwgCiAgICAgICAgICAgICBjb2wgPSBzZWFib3JuX3BhbGV0dGVbMTozXSwKICAgICAgICAgICAgIHlheGlzLnJpZ2h0PUZBTFNFLAogICAgICAgICAgICAgYXhlcz1GQUxTRSwKICAgICAgICAgICAgIGx3ZD0xLAogICAgICAgICAgICAgY2V4Lm1haW49MywKICAgICAgICAgICAgIG1ham9yLnRpY2tzPSJ5ZWFycyIsCiAgICAgICAgICAgICBtYWpvci5mb3JtYXQ9IiVZIiwKICAgICAgICAgICAgIG1pbm9yLmZvcm1hdD0iJVkiLAogICAgICAgICAgICAgZ3JpZC50aWNrcy5sdHk9MSkpCiAgCn0pCmBgYAoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vd3d3LmZ3cy5nb3YvZW5kYW5nZXJlZC8iIHRhcmdldD0iYmxhbmsiPjxpbWcgc3JjPSJJTUFHRVMvdXNfZndzLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2E+PC9kaXY+CiMjIyBVLlMuIEZXUyBFbmRhbmdlcmVkIFNwZWNpZXMgTGlzdAoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIENPTkNBVCgoZi5kYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSBBUyBkZWNhZGUsIAogICAgICAgICAgICAgICBmLnRheG9ub21pY19ncm91cCwKICAgICAgICAgICAgICAgZi5jdXJyZW50X3N0YXR1cywKICAgICAgICAgICAgICAgY291bnQoKikgQVMgc3BlY2llc19jb3VudAogICAgICAgIEZST00gZndzX3NwZWNpZXNfeWVhciBmCiAgICAgICAgV0hFUkUgZi5jdXJyZW50X3N0YXR1cyBJTiAoJ0VuZGFuZ2VyZWQnLCAnUmVjb3ZlcnknLCAnUmVzb2x2ZWQgVGF4b24nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVGhyZWF0ZW5lZCcsICdFeHRpbmN0aW9uJykKICAgICAgICBHUk9VUCBCWSBDT05DQVQoKGYuZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJyksCiAgICAgICAgICAgICAgICAgZi50YXhvbm9taWNfZ3JvdXAsCiAgICAgICAgICAgICAgICAgZi5jdXJyZW50X3N0YXR1cwogICAgICAgIE9SREVSIEJZIENPTkNBVCgoZi5kYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSwKICAgICAgICAgICAgICAgICBmLnRheG9ub21pY19ncm91cCwKICAgICAgICAgICAgICAgICBmLmN1cnJlbnRfc3RhdHVzIgoKZndzX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGZ3c19kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZ3c19maWcsIGZpZy5oZWlnaHQgPSAxNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKcGFyKG1mcm93PWMoNSwzKSwgbWFyPWMoNSwgNSwgMiwgMSkpCm91dHB1dCA8LSBieShmd3NfZGYsIGZ3c19kZiR0YXhvbm9taWNfZ3JvdXAsIGZ1bmN0aW9uKHN1YikgewogIHBsb3RfbWF0IDwtIHdpdGgoc3ViLCB0YXBwbHkoc3BlY2llc19jb3VudCwgbGlzdChkZWNhZGUsIGN1cnJlbnRfc3RhdHVzKSwgc3VtKSkKICAKICBiYXJwbG90KHBsb3RfbWF0LCBtYWluPXN1YiR0YXhvbm9taWNfZ3JvdXBbWzFdXSwgY2V4Lm1haW49MS41LAogICAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHBsb3RfbWF0LCBuYS5ybT1UUlVFKSs1KSwKICAgICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbc2VxX2Fsb25nKHJvdy5uYW1lcyhwbG90X21hdCkpXSwgYmVzaWRlPVRSVUUpCiAgbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVtzZXFfYWxvbmcocm93Lm5hbWVzKHBsb3RfbWF0KSldLCAKICAgICAgICAgbmNvbD1sZW5ndGgocm93Lm5hbWVzKHBsb3RfbWF0KSkpCiAgYm94KCkKfSkKYGBgCgoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vd3d3Lm5hc3MudXNkYS5nb3YvQWdDZW5zdXMvIiB0YXJnZXQ9ImJsYW5rIj48aW1nIHNyYz0iSU1BR0VTL3VzZGEucG5nIiB3aWR0aD0iMTAwcHgiLz48L2E+PC9kaXY+CiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5VLlMuIERlcGFydG1lbnQgb2YgQWdyaWN1bHR1cmU6IEFncmljdWx0dXJlIENlbnN1czwvc3Bhbj4KCmBgYHtyIGZpZzQsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNywgZmlnLmFsaWduID0gImNlbnRlciJ9CnNxbCA8LSAiU0VMRUNUIHllYXIsCiAgICAgICAgICAgICAgIENBU0UKICAgICAgICAgICAgICAgICAgICBXSEVOIGRvbWFpbl9jYXRlZ29yeSA9ICcnIFRIRU4gJ1RPVEFMXG5PUEVSQVRJT05TJwogICAgICAgICAgICAgICAgICAgIEVMU0UgUkVQTEFDRShSRVBMQUNFKFJFUExBQ0UoZG9tYWluX2NhdGVnb3J5LCAnKCcsICdcbignKSwgJ1RPJywgJ1RPXG4nKSwgJ09SJywgJ09SXG4nKQogICAgICAgICAgICAgICBFTkQgQVMgZG9tYWluX2NhdGVnb3J5LCAKICAgICAgICAgICAgICAgdmFsdWUKICAgICAgICBGUk9NIGFnX2NlbnN1cyAKICAgICAgICBXSEVSRSBkYXRhX2l0ZW0gPSAnRkFSTSBPUEVSQVRJT05TIC0gTlVNQkVSIE9GIE9QRVJBVElPTlMnIgoKYWdjZW5zdXNfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgpwbG90X21hdCA8LSB3aXRoKGFnY2Vuc3VzX2RmLCB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgZG9tYWluX2NhdGVnb3J5KSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA1LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBBZ3JpY3VsdHVyZSBDZW5zdXM6IE51bWJlciBvZiBGYXJtIE9wZXJhdGlvbnMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgMkU2KzVFNSksIGJlc2lkZT1UUlVFLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpheGlzKHNpZGU9MSwgYXQ9YygzLDksMTUsMjEsMjcsMzMsMzksNDUpKzAuNSwgbGFiZWxzPWNvbG5hbWVzKHBsb3RfbWF0KSwgdGljaz1GQUxTRSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKYGBgCgpgYGB7cn0Kc3FsIDwtICJXSVRIIHN1YiBBUyAoCiAgICAgICAgICBTRUxFQ1QgeWVhciwKICAgICAgICAgICAgICAgICBkYXRhX2l0ZW0sCiAgICAgICAgICAgICAgICAgUkVQTEFDRSgKICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRvbWFpbl9jYXRlZ29yeSwgJzsnLCAnLCcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKCcsICdcbignKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdUTycsICdUT1xuJyksCiAgICAgICAgICAgICAgICAgICAgICAgICcgT1InLCAnIE9SXG4nKSwKICAgICAgICAgICAgICAgICAgICAnTEVTUycsICdMRVNTXG4nKSBBUyBkb21haW5fY2F0ZWdvcnksIAogICAgICAgICAgICAgICAgIHZhbHVlCiAgICAgICAgICBGUk9NIGFnX2NlbnN1cyAKICAgICAgICAgIFdIRVJFIGRhdGFfaXRlbSBMSUtFICclQ09NTU9ESVRZIFRPVEFMUyUnCiAgICAgICAgICAgIEFORCB2YWx1ZSBJUyBOT1QgTlVMTAogICAgICAgKSAKCiAgICAgICBTRUxFQ1QgeWVhciwgCiAgICAgICAgICAgICAgQ0FTRQogICAgICAgICAgICAgICAgICBXSEVOIFRSSU0oZG9tYWluX2NhdGVnb3J5KSA9ICcnIAogICAgICAgICAgICAgICAgICBUSEVOIENBU0UgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBXSEVOIGRhdGFfaXRlbSA9ICdDT01NT0RJVFkgVE9UQUxTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQnIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEhFTiAnVE9UQUxcblNBTEVTJwogICAgICAgICAgICAgICAgICAgICAgICAgICAgV0hFTiBkYXRhX2l0ZW0gPSAnQ09NTU9ESVRZIFRPVEFMUyAtIFNBTEVTOyBNRUFTVVJFRCBJTiAkIC8gT1BFUkFUSU9OJyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRIRU4gJ1RPVEFMXG5TQUxFU1xuUEVSIE9QRVJBVElPTicKICAgICAgICAgICAgICAgICAgICAgICBFTkQKICAgICAgICAgICAgICAgICAgRUxTRSBkb21haW5fY2F0ZWdvcnkgCiAgICAgICAgICAgICAgRU5EIEFTIGRvbWFpbl9jYXRlZ29yeSwKICAgICAgICAgICAgICB2YWx1ZQogICAgICAgRlJPTSBzdWIKICAgICAgIgoKYWdjZW5zdXNfZGYgPC0gd2l0aGluKGRiR2V0UXVlcnkoY29ubiwgc3FsKSwKICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbl9jYXRlZ29yeSA8LSBmYWN0b3IoZG9tYWluX2NhdGVnb3J5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJGQVJNIFNBTEVTOiBcbihMRVNTXG4gVEhBTiAyLDUwMCAkKSIsICJGQVJNIFNBTEVTOiBcbigyLDUwMCBUT1xuIDQsOTk5ICQpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZBUk0gU0FMRVM6IFxuKDUsMDAwIFRPXG4gOSw5OTkgJCkiLCAiRkFSTSBTQUxFUzogXG4oMTAsMDAwIFRPXG4gMjQsOTk5ICQpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFSTSBTQUxFUzogXG4oMjUsMDAwIFRPXG4gNDksOTk5ICQpIiwgIkZBUk0gU0FMRVM6IFxuKDUwLDAwMCBUT1xuIDk5LDk5OSAkKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZBUk0gU0FMRVM6IFxuKDEwMCwwMDAgVE9cbiA0OTksOTk5ICQpIiwgIkZBUk0gU0FMRVM6IFxuKDUwMCwwMDAgT1JcbiBNT1JFICQpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRPVEFMXG5TQUxFUyIsICJUT1RBTFxuU0FMRVNcblBFUiBPUEVSQVRJT04iKSkKKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGFnY2Vuc3VzX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnNSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE3LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChhZ2NlbnN1c19kZlthZ2NlbnN1c19kZiRkb21haW5fY2F0ZWdvcnkgIT0gJ1RPVEFMXG5TQUxFUycsXSwgCiAgICAgICAgICAgICAgICAgdGFwcGx5KHZhbHVlLCBsaXN0KHllYXIsIGZhY3Rvcihkb21haW5fY2F0ZWdvcnkpKSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA1LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBBZ3JpY3VsdHVyZSBDZW5zdXM6IENvbW1vZGl0eSBUb3RhbHMgLSBGYXJtIFNhbGVzIiwgY2V4Lm1haW49MS41LAogICAgICAgIGNvbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgeWxpbT1jKDAsIDFFNiksIGJlc2lkZT1UUlVFLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpheGlzKHNpZGU9MSwgYXQ9YygzLDksMTUsMjEsMjcsMzMsMzksNDUsNTEpKzAuNSwgbGFiZWxzPWNvbG5hbWVzKHBsb3RfbWF0KSwgdGljaz1GQUxTRSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKYGBgCgoKYGBge3IgZmlnNiwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChhZ2NlbnN1c19kZlthZ2NlbnN1c19kZiRkb21haW5fY2F0ZWdvcnkgPT0gJ1RPVEFMXG5TQUxFUycsXSwgCiAgICAgICAgICAgICAgICAgdGFwcGx5KHZhbHVlLCBsaXN0KHllYXIsIGZhY3Rvcihkb21haW5fY2F0ZWdvcnkpKSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA4LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBBZ3JpY3VsdHVyZSBDZW5zdXM6IENvbW1vZGl0eSBUb3RhbHMgLSBPdmVyYWxsIEZhcm0gU2FsZXMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgNEUxMSs1RTEwKSwgYmVzaWRlPVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIiwgc3BhY2U9MikKICAgICAgICAKYXhpcyhzaWRlPTEsIGF0PWMoMi41LDUuNSw4LjUsMTEuNSwxNC41KSwgbGFiZWxzPXJvdy5uYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIHllYXIsIAogICAgICAgICAgICAgICBSRVBMQUNFKFJFUExBQ0UoZGF0YV9pdGVtLCAnIC0gT1BFUkFUSU9OUyBXSVRIIEFSRUEgSEFSVkVTVEVEJywgJycpLCAnOyBHUkFJTicsICcnKSBhcyBjcm9wLAogICAgICAgICAgICAgICB2YWx1ZQogICAgICAgIEZST00gYWdfY2Vuc3VzCiAgICAgICAgV0hFUkUgZGF0YV9pdGVtIElOICgnQ09STjsgR1JBSU4gLSBPUEVSQVRJT05TIFdJVEggQVJFQSBIQVJWRVNURUQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1dIRUFUIC0gT1BFUkFUSU9OUyBXSVRIIEFSRUEgSEFSVkVTVEVEJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTT1lCRUFOUyAtIE9QRVJBVElPTlMgV0lUSCBBUkVBIEhBUlZFU1RFRCcpCiAgICAgICAgT1JERVIgQlkgeWVhciwgZGF0YV9pdGVtCiAgICAgICAiCgpjcm9wc19kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChjcm9wc19kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzcsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBsb3RfbWF0IDwtIHdpdGgoY3JvcHNfZGYsIHRhcHBseSh2YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHllYXIsIGZhY3Rvcihjcm9wLCBsZXZlbHM9YygiV0hFQVQiLCAiU09ZQkVBTlMiLCAiQ09STiIpKSksIHN1bSkKKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogTnVtYmVyIG9mIE9wZXJhdGlvbnMgYnkgU3BlY2lmaWMgQ3JvcHMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgNUU1KSwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCmBgYHtyfQpzcWwgPC0gIlNFTEVDVCB5ZWFyLCAKICAgICAgICAgICAgICAgUkVQTEFDRSgKICAgICAgICAgICAgICAgICAgUkVQTEFDRSgKICAgICAgICAgICAgICAgICAgICAgUkVQTEFDRShkYXRhX2l0ZW0sICcgLSBPUEVSQVRJT05TIFdJVEggU0FMRVMnLCAnJyksIAogICAgICAgICAgICAgICAgICAgICAnOyBJTkNMIENBTFZFUycsICcnKSwKICAgICAgICAgICAgICAgICAgJzsgQlJPSUxFUlMnLCAnJykgYXMgbGl2ZXN0b2NrLAogICAgICAgICAgICAgICB2YWx1ZQogICAgICAgIEZST00gYWdfY2Vuc3VzCiAgICAgICAgV0hFUkUgZGF0YV9pdGVtIElOICgnQ0FUVExFOyBJTkNMIENBTFZFUyAtIE9QRVJBVElPTlMgV0lUSCBTQUxFUycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSE9HUyAtIE9QRVJBVElPTlMgV0lUSCBTQUxFUycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQ0hJQ0tFTlM7IEJST0lMRVJTIC0gT1BFUkFUSU9OUyBXSVRIIFNBTEVTJykKICAgICAgICBPUkRFUiBCWSB5ZWFyLCBkYXRhX2l0ZW0KICAgICAgICIKCmxpdmVzdG9ja19kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChsaXZlc3RvY2tfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgoKYGBge3IgZmlnOCwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChsaXZlc3RvY2tfZGYsIHRhcHBseSh2YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHllYXIsIGZhY3RvcihsaXZlc3RvY2ssIGxldmVscz1jKCAiQ0hJQ0tFTlMiLCAiSE9HUyIsICJDQVRUTEUiKSkpLCBzdW0pCikKCnBhcihtYXI9Yyg1LCA4LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBBZ3JpY3VsdHVyZSBDZW5zdXM6IE51bWJlciBvZiBPcGVyYXRpb25zIGJ5IFNwZWNpZmljIExpdmVzdG9jayIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCAxRTYrMkU1KSwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgeWVhciwgCiAgICAgICAgICAgICAgIENBU0UKICAgICAgICAgICAgICAgICAgIFdIRU4gZGF0YV9pdGVtID0gJ0NST1AgVE9UQUxTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQnIFRIRU4gJ0NST1AgVE9UQUxTJwogICAgICAgICAgICAgICAgICAgV0hFTiBkYXRhX2l0ZW0gPSAnQU5JTUFMIFRPVEFMUzsgSU5DTCBQUk9EVUNUUyAtIFNBTEVTOyBNRUFTVVJFRCBJTiAkJyBUSEVOICdBTklNQUwgVE9UQUxTJwogICAgICAgICAgICAgICBFTkQgYXMgY2F0ZWdvcnksCiAgICAgICAgICAgICAgIHZhbHVlCiAgICAgICAgRlJPTSBhZ19jZW5zdXMKICAgICAgICBXSEVSRSBkYXRhX2l0ZW0gSU4gKCdDUk9QIFRPVEFMUyAtIFNBTEVTOyBNRUFTVVJFRCBJTiAkJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICdBTklNQUwgVE9UQUxTOyBJTkNMIFBST0RVQ1RTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQnKQogICAgICAgIE9SREVSIEJZIHllYXIsIGRhdGFfaXRlbQogICAgICAgIgoKYWd0b3RhbF9kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChhZ3RvdGFsX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnOSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE1LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KcGxvdF9tYXQgPC0gd2l0aChhZ3RvdGFsX2RmLCB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgY2F0ZWdvcnkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogQ3JvcCB2cyBBbmltYWwgU2FsZXMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgMkUxMSs1RTEwKSwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL3dvcmxkX21ldHJpY3MucG5nIiB3aWR0aD0iMzAwcHgiLz48L2Rpdj4KIyMjIFdvcmxkIE1ldHJpY3MKCmBgYHtyfQpzcWwgPC0gIldJVEggcG9wIEFTIAogICAgICAgICAgICAoU0VMRUNUIHAueWVhciwKICAgICAgICAgICAgICAgICAgICBwLnBvcHVsYXRpb24KICAgICAgICAgICAgIEZST00gd29ybGRfcG9wdWxhdGlvbiBwCiAgICAgICAgICAgICBXSEVSRSBwLmNvdW50cnlfbmFtZSA9ICdXb3JsZCcKICAgICAgICAgICAgICAgQU5EIHAueWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgbGFuZCBBUwogICAgICAgICAgICAoU0VMRUNUIGEueWVhciwKICAgICAgICAgICAgICAgICAgICBhLnBlcmNlbnRfYXJhYmxlCiAgICAgICAgICAgICBGUk9NIGFyYWJsZV9sYW5kIGEKICAgICAgICAgICAgIFdIRVJFIGEuY291bnRyeV9uYW1lID0gJ1dvcmxkJwogICAgICAgICAgICAgICBBTkQgYS55ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgKSwKICAgICAgICAgCiAgICAgICAgICAgICAgZmF1bmEgQVMKICAgICAgICAgICAgKFNFTEVDVCBpLnllYXIsCiAgICAgICAgICAgICAgICAgICAgU1VNKGkuc3BlY2llc19jb3VudCkgQVMgYW5pbWFsc19jb3VudAogICAgICAgICAgICAgRlJPTSBpdWNuX3NwZWNpZXNfY291bnQgaQogICAgICAgICAgICAgV0hFUkUgaS55ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgR1JPVVAgQlkgaS55ZWFyCiAgICAgICAgICAgICApLAogICAgICAgICAKICAgICAgICAgICAgICBmbG9yYSBBUwogICAgICAgICAgICAoU0VMRUNUIHAuYXNzZXNzbWVudF95ZWFyIGFzIHllYXIsCiAgICAgICAgICAgICAgICAgICAgQ09VTlQoKikgQVMgcGxhbnRzX2NvdW50CiAgICAgICAgICAgICBGUk9NIHBsYW50c19hc3Nlc3NtZW50cyBwCiAgICAgICAgICAgICBXSEVSRSBwLmFzc2Vzc21lbnRfeWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgICAgQU5EIHAuaW50ZXJwcmV0ZWRfc3RhdHVzID0gJ1RocmVhdGVuZWQnCiAgICAgICAgICAgICBHUk9VUCBCWSBwLmFzc2Vzc21lbnRfeWVhcgogICAgICAgICAgICAgKSwgICAgICAgICAgICAgCiAgICAgICAgIAogICAgICAgICAgICAgIGljZSBBUwogICAgICAgICAgICAoU0VMRUNUIHMuZGF0ZV95ZWFyIGFzIHllYXIsCiAgICAgICAgICAgICAgICAgICAgQVZHKHMuZXh0ZW50KSBGSUxURVIoV0hFUkUgcy5yZWdpb24gPSAnQXJjdGljJykgQVMgYXJjdGljX3NlYV9pY2VfZXh0ZW50LAogICAgICAgICAgICAgICAgICAgIEFWRyhzLmV4dGVudCkgRklMVEVSKFdIRVJFIHMucmVnaW9uID0gJ0FudGFyY3RpY2EnKSBBUyBhbnRhcmN0aWNfc2VhX2ljZV9leHRlbnQKICAgICAgICAgICAgIEZST00gc2VhX2ljZV9leHRlbnQgcwogICAgICAgICAgICAgV0hFUkUgcy5kYXRlX3llYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICBHUk9VUCBCWSBzLmRhdGVfeWVhcgogICAgICAgICAgICAgKSwKICAgICAgICAgICAgIAogICAgICAgICAgICAgIG9jZWFuIEFTCiAgICAgICAgICAgIChTRUxFQ1Qgby55ZWFyIGFzIHllYXIsCiAgICAgICAgICAgICAgICAgICAgQVZHKG8udGNvMikgQVMgdG90YWxfY28yLAogICAgICAgICAgICAgICAgICAgIEFWRyhvLnBodHMyNXAwKSBBUyBwaF9zY2FsZQogICAgICAgICAgICAgRlJPTSBvY2Vhbl9kYXRhIG8KICAgICAgICAgICAgIFdIRVJFIG8ueWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgICAgQU5EIG8udGNvMiA8PiAtOTk5OSBBTkQgby5waHRzMjVwMCA8PiAtOTk5OQogICAgICAgICAgICAgR1JPVVAgQlkgby55ZWFyCiAgICAgICAgICAgICApLAogICAgICAgICAgICAgCiAgICAgICAgICAgICAgdGVtcCBBUwogICAgICAgICAgICAoU0VMRUNUIGcueWVhciBhcyB5ZWFyLAogICAgICAgICAgICAgICAgICAgIEFWRyhnLmdsb2JhbF9tZWFuKSBBUyBnbG9iYWxfbWVhbgogICAgICAgICAgICAgRlJPTSBnbG9iYWxfdGVtcGVyYXR1cmUgZwogICAgICAgICAgICAgV0hFUkUgZy55ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgR1JPVVAgQlkgZy55ZWFyCiAgICAgICAgICAgICApCiAgICAgICAgICAgICAKICAgICAgICAgU0VMRUNUIHBvcC55ZWFyLCBwb3AucG9wdWxhdGlvbiwgbGFuZC5wZXJjZW50X2FyYWJsZSwgZmF1bmEuYW5pbWFsc19jb3VudCwgZmxvcmEucGxhbnRzX2NvdW50LAogICAgICAgICAgICAgICAgaWNlLmFyY3RpY19zZWFfaWNlX2V4dGVudCwgaWNlLmFudGFyY3RpY19zZWFfaWNlX2V4dGVudCwgCiAgICAgICAgICAgICAgICBvY2Vhbi50b3RhbF9jbzIsIG9jZWFuLnBoX3NjYWxlLCB0ZW1wLmdsb2JhbF9tZWFuCiAgICAgICAgIEZST00gcG9wIAogICAgICAgICBKT0lOIGxhbmQgVVNJTkcgKHllYXIpCiAgICAgICAgIEpPSU4gZmF1bmEgVVNJTkcgKHllYXIpCiAgICAgICAgIEpPSU4gZmxvcmEgVVNJTkcgKHllYXIpCiAgICAgICAgIEpPSU4gaWNlIFVTSU5HICh5ZWFyKQogICAgICAgICBKT0lOIG9jZWFuIFVTSU5HICh5ZWFyKQogICAgICAgICBKT0lOIHRlbXAgVVNJTkcgKHllYXIpCiAgICAgICAgIE9SREVSIEJZIHBvcC55ZWFyIgoKZW52X3dvcmxkX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGVudl93b3JsZF9kZikpLCBmb250X3NpemUgPSAxMiwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7cn0Ka2FibGVfc3R5bGluZyhrYWJsZShjb3IoZW52X3dvcmxkX2RmWy0xXSkpLCBmb250X3NpemUgPSAxMiwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBmaWcxMCwgZmlnLmhlaWdodCA9IDE1LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBhcihtZnJvdz1jKDQsMiksIG1haSA9IGMoMC4zLCAwLjIsIDAuNywgMC4yKSkKCmZvcih4IGluIGNvbG5hbWVzKGVudl93b3JsZF9kZilbMzpuY29sKGVudl93b3JsZF9kZildKSB7CiAgbGZpdCA8LSBsb2VzcyhwYXN0ZSh4LCAifiBwb3B1bGF0aW9uIiksIGRhdGE9ZW52X3dvcmxkX2RmKQogIAogIHBsb3QoYXMuZm9ybXVsYShwYXN0ZSh4LCAifiBwb3B1bGF0aW9uIikpLCBlbnZfd29ybGRfZGYsIAogICAgICAgbWFpbj1wYXN0ZSgicG9wdWxhdGlvbiBhbmRcbiIsIGdzdWIoIl8iLCAiICIsIHgpKSwgY2V4Lm1haW49MiwKICAgICAgIHR5cGU9InAiLCBjb2w9c2VhYm9ybl9wYWxldHRlWzFdLCB5YXh0PSduJywgeGF4dD0nbicsIHBjaD0xOSkKICBheGlzKHNpZGU9MSwgYXQ9YXhUaWNrcygxKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDEpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpKQogIGF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSkpCiAgcG9wX29yZGVyIDwtIG9yZGVyKGVudl93b3JsZF9kZiRwb3B1bGF0aW9uKQogIGxpbmVzKGVudl93b3JsZF9kZiRwb3B1bGF0aW9uW3BvcF9vcmRlcl0sIGxmaXQkZml0dGVkW3BvcF9vcmRlcl0sIGNvbD1zZWFib3JuX3BhbGV0dGVbNF0sIGx3ZD0zKQp9CgpgYGAKCgpgYGB7cn0KZGJEaXNjb25uZWN0KGNvbm4pCmBgYAoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxpbWcgc3JjPSJJTUFHRVMvcl9zaGlueS5wbmciLz48L2Rpdj4KCiMjIyBVc2VyIERhdGEgQXBwbGljYXRpb24KCjxkaXYgc3R5bGU9ImZsb2F0OmNlbnRlciI+PGltZyBzcmM9IklNQUdFUy9lbnZfZGF0YS5wbmciIHdpZHRoPSI0MDBweCIvPjwvZGl2PgoKYGBge3J9CgojIExJTlVYIFNIRUxMIENPTU1BTkQgQ0FMTApzeXN0ZW0ocGFzdGUwKCJnbm9tZS10ZXJtaW5hbCAtLSBSc2NyaXB0IC1lIFwibGlicmFyeShzaGlueSk7IHNldHdkKCciLCBnZXR3ZCgpLCAiJyk7IHJ1bkFwcCgnRW52aXJvbm1lbnREQl9TaGlueV9BcHAuUicpXCIiKSkKCmBgYAoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKPGgyPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNvbmNsdXNpb248L3NwYW4+PC9oMj4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0OyI+PGltZyBzcmM9IklNQUdFUy9kYXRhX3BpcGVsaW5lLnBuZyIgd2lkdGg9IjM1MHB4Ii8+PC9kaXY+Cjxici8+Cgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0OyI+PGltZyBzcmM9IklNQUdFUy9wb3N0Z3Jlc3FsX3IucG5nIiB3aWR0aD0iMTAwcHgiLz48L2Rpdj4KPGJyLz4KPGJyLz4KCi0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlBvc3RncmVzIGFzIHRvb2wgaW4gZGF0YSBzY2llbmNlIHdvcmtmbG93PC9oMz4KLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+RGF0YSBwZXJzaXN0ZW5jZSBhbmQgaHlnaWVuZTwvaDM+Ci0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNlbnRyYWxpemF0aW9uIGFuZCBzZWN1cml0eTwvaDM+Ci0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkVmZmljaWVuY3kgYW5kIHVzZWZ1bG5lc3M8L2gzPgotIDxoMz48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5TY2FsYWJpbGl0eSBhbmQgYXBwbGljYWJpbGl0eTwvaDM+Cgo8YnIvPgo8YnIvPgoKCg==